﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RbmlVisualizer.Data;
using System.Text.RegularExpressions;

namespace RbmlVisualizer.Parser
{
    public class RBMLTextParser : TextParser
    {

        public override ClassDiagram ParseDiagramFromFile(Uri filename)
        {
            this.cdClassDiagram = new ClassDiagram();
            this.cdClassDiagram.ClassDiagramType = ClassDiagramType.Rbml;

            this.alLinesInFile = ReadFile(filename);

            int iLineNumber = 1;
            foreach (string line in this.alLinesInFile)
            {
                RegExLine(line.Trim(), iLineNumber);
                iLineNumber++;
            }

            FillLinkEntities();

            return this.cdClassDiagram;
        }

        public Data.ClassDiagram ParseDiagramFromFile(Uri filename, string fileType)
        {
            throw new NotImplementedException();
        }

        private void RegExLine(string line, int lineNumber)
        {
            bool bMatchedLine = false;
            //try and read @startuml
            if (!this.bStartUMLFound)
                bMatchedLine = FindStartUML(line);

            //read the PlantUML block between @startuml and @enduml
            if (this.bStartUMLFound && !this.bEndUMLFound && !bMatchedLine)
            {
                //try and read @enduml
                bMatchedLine = FindEndUML(line);
                if(!bMatchedLine)
                    bMatchedLine = ReadRBML(line);
            }
            if (!bMatchedLine && !string.IsNullOrEmpty(line))
                Logging.LoggingService.Instance.WriteInfo("Possible Syntax Error at line number {0} \n{1}", lineNumber, line);
        }

        private bool ReadRBML(string line)
        {
            bool bMatchedLine = false;
            if (!bOpenCurly) //we do not have an Entity Declaration open curly brace
            {
                bMatchedLine = ReadRelationship(line);

                if (!bMatchedLine)
                    bMatchedLine = ReadEntityDeclaration(line);
            }
            else //we have an open curly brace, so get the methods or find the closing curly brace
            {
                bMatchedLine = ReadEntityMethodOrField(line);

                if (!bMatchedLine)
                    bMatchedLine = ReadEntityCloseScope(line);
            }

            return bMatchedLine;
        }

        private bool ReadEntityCloseScope(string line)
        {
            #region Class Declaration (with Open Curly)
            string pattern = @"(?<CloseCurly>^})";
            Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
            MatchCollection matches = rgx.Matches(line);

            if (matches.Count > 0)
            {
                foreach (Match match in matches)
                {
                    GroupCollection groups = match.Groups;
                    this.bOpenCurly = false;
                }
                this.cdClassDiagram.Entities.Add(this.entity);
                //I have matched close curly set open curly to false and retrn out.
                return true;
            }
            //I have not matched line
            return false;
            #endregion
        }

        private bool ReadEntityMethodOrField(string line)
        {
            EntityPart entityPart;

            #region Entity Methods or Fields
            //string pattern = @"(?<MemberModifier>^(-|#|~|\+)\s+\b)(?<MemberID>\w+)\s*(?<ArgumentList>(?<OpenParen>\()(?<Argument>(?<Seperator>,\s*)?(?<Type>\w+)(?<Name>\s+\w+)?)*(?<CloseParen>\)))?";
            string pattern = @"(?<FieldOrMember>[^\r\n}]+)";
            Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
            MatchCollection matches = rgx.Matches(line);

            if (matches.Count > 0)
            {
                entityPart = new EntityPart();
                foreach (Match match in matches)
                {
                    GroupCollection groups = match.Groups;
                    entityPart.Value = groups["FieldOrMember"].Value.Trim();
                }
                this.entity.EntityParts.Add(entityPart);
                //I have matched a feild or method line, just return.
                return true;
            }
            //I have not matched a line
            return false;
            #endregion
        }

        private bool ReadEntityDeclaration(string line)
        {
            #region Class Declaration (with Open Curly)
            string pattern = @"(?<EntityType>(interface|enum|abstract\s+class|abstract|class))\s+(?<Entity>\""(?<EntityID>[^""\r\n]*)\"")\s+(?<OpenCurly>{)\s*";
            Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
            MatchCollection matches = rgx.Matches(line);

            if (matches.Count > 0)
            {
                this.entity = new Entity();
                this.entity.EntityParts = new List<IEntityPart>();
                foreach (Match match in matches)
                {
                    GroupCollection groups = match.Groups;
                    this.entity.EntityType = GetEntityType(groups["EntityType"].Value.Trim());
                    this.entity.Uid = groups["EntityID"].Value.Trim();
                    this.bOpenCurly = true;
                }
                //I have matched a class declaration with an open curly brace, return out.
                return true;
            }
            //I have not matched line
            return false;
            #endregion
        }

        private bool ReadRelationship(string line)
        {

            Data.Link.Link link;
            #region Relationships
            string pattern = @"(?<EntityOne>\""(?<EntityOneID>[^""\r\n]*)\"")\s*(?<MultiplicityOne>\""(?<MultiOneValue>[^""\r\n]*)\"")?\s+(?<LinkType>(?<LinkEndType1>(o|\*|\<(\|)?)?)(?<LinkStyle>(--|\.\.))(?<LinkEndType2>(o|\*|(\|)?\>)?))\s+(?<MultiplicityTwo>\""(?<MultiTwoValue>[^""\r\n]*)\"")?\s*(?<EntityTwo>\""(?<EntityTwoID>[^""\r\n]*)\"")(?<RelationshipLabel>\s*\:\s*(?<Label>[^\r\n]*))?";
            Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
            MatchCollection matches = rgx.Matches(line);

            if (matches.Count > 0)
            {
                link = new Data.Link.Link();
                foreach (Match match in matches)
                {
                    GroupCollection groups = match.Groups;
                    link.EntityOne = new Entity();
                    link.EntityOne.Uid = groups["EntityOneID"].Value.Trim();

                    link.LinkEndLabel1 = GetLinkEndLabel(groups["MultiOneValue"].Value.Trim());

                    link.LinkEndType1 = GetLinkEndType(groups["LinkEndType1"].Value.Trim());
                    link.LinkEndType2 = GetLinkEndType(groups["LinkEndType2"].Value.Trim());
                    link.LinkEndLabel2 = GetLinkEndLabel(groups["MultiTwoValue"].Value.Trim());
                    link.EntityTwo = new Entity();
                    link.EntityTwo.Uid = groups["EntityTwoID"].Value.Trim();
                    link.Label = groups["Label"].Value.Trim();
                }
                this.cdClassDiagram.Links.Add(link);
                //I have matched a relationship line, just return.
                return true;
            }
            //I have not matched line
            return false;
            #endregion
        }


    }
}
